<?php
/**
 * URI Rule
 * Validates that a value is a valid URI.
 *
 * @package     Q-Validate
 * @subpackage  Rules
 * @author      Luke Visinoni <luke.visinoni@gmail.com>
 * @copyright   (c) 2010 Luke Visinoni <luke.visinoni@gmail.com>
 * @todo        Unit tests
 * @todo        Finish and test conditionals for all combinations of parameters
 * @version     $Id: Uri.php 44 2011-01-26 04:51:58Z luke.visinoni $
 */
class Q_Rule_Uri extends Q_Rule_Abstract {

    const PATTERN_HOST = '([a-z][-a-z0-9]*\.)+((aero|arpa|a[cdefgilmnoqrstuwxz])|(biz|b[abdefghijmnorstvwyz])|(cat|com|coop|c[acdfghiklmnorsuvxyz])|d[ejkmoz]|(edu|e[ceghrstu])|f[ijkmor]|(gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(info|int|i[delmnoqrst])|(jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(mil|mobi|museum|m[acdghklmnopqrstuvwxyz])|(name|net|n[acefgilopruz])|(om|org)|(pro|p[aefghklmnrstwy])|qa|r[eouw]|s[abcdeghijklmnortvyz]|(travel|t[cdfghjklmnoprtvwz])|u[agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw])';
    
    const PATTERN_PATH = '(?:\/[^\/?#]*)+';
    
    const PATTERN_SCHEME = '[a-z0-9]+:\/\/';
    
    const PATTERN_QUERY = '\?([^=#\n]+=[^&#\n]+&?)*';
    
    /**
     * Parameters
     * scheme - if specified, will require that the URI has a specific scheme
     *          if set to false, scheme will not be required at all
     * path   - if set to false, uri cannot have a path
     */
    protected $_params = array(
        'scheme' => null,
        'query' => null,
        'path' => null,
    );
    
    public function isValid($value) {
    
        $scheme = $this->getParam('scheme');
        $path = $this->getParam('path');
        $query = $this->getParam('query');
        
        $pattern = '';
        if ($scheme === false) {
            // scheme is not required
            
        } elseif (is_null($scheme)) {
            // scheme is required, but it doesn't matter what
            $pattern .= sprintf("(%s)?", self::PATTERN_SCHEME);
        } elseif ($scheme === true) {
            $pattern .= self::PATTERN_SCHEME;
        } elseif (is_array($scheme)) {
            // scheme must be one of the array values
            $pattern = sprintf("(%s):\/\/", implode('|', $scheme));
        } else {
            // scheme must be the value
            $pattern = (string) $scheme;
        }
        
        $pattern .= self::PATTERN_HOST;
        
        if (is_null($path)) {
            $pattern .= sprintf("(%s)?", self::PATTERN_PATH);
        } elseif ($path === false) {
            // no path required
        } elseif ($path === true) {
            $pattern .= self::PATTERN_PATH;
        }
        
        $pattern = sprintf("~^%s$~i", $pattern);
        return $this->_validatePattern($pattern, $value);
    
    }

}
